/**
* @file
* @brief This file handles all server related functions.
*/

/*
 For now, jump to processconfigline() below and read Jerry's Task 1
 Then jump to file database.h and read Jerry's Task 2
 Note: This does not mean u have 2 different tasks, you have the same only one current task, implement processconfigline() properly
 but the numbers are just to guide you so you know the order you have to read from what I am telling u to do.

Note: I may be wrong, ask me if you feel something should be change with my methods
 and dont just implement what i tell you, understand why and let me know if you dont get what I am saying
 cause when i code stuff that depends on what you're doing, i'll assume that you'll implement exactly as i told you too
Note: Don't delete any of my comments for you, once you're done, I'll need to refer to them
 to make sure you accounted for all the cases i told you about, i'll delete these comments as I confirm they are implemented
Jerry Task 1: server.c
Jerry Task 2: database.h
Jerry Task 3: utils.c
@note: These are not 3 tasks, it is just one taks which is implement processing the new configuration file.
*/


//-------------------------------------------------------s
// Header Files
//-------------------------------------------------------
#include "server.h" // for client structure
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define MAX_LEN 80 //Maximum length of message
#include <unistd.h> // for getopt
#include <limits.h> // for PATH_MAX
#include <ctype.h> // for isprint()
// Flex/ Bison parsing occurs in utils.c where it includes the proper header files compiled properly in Makefile


//-------------------------------------------------------
// Constants
//-------------------------------------------------------
#define MAX_LISTENQUEUELEN 20   // The maximum number of queued connections.
#define LOGGING 1
    ///< 0 -> disabled logging,
    ///< 1 -> log to stdout,
    ///< 2 -> log to file, that follows format: Client/Server-yyyy-mm-dd-hh-ss.log
//-------------------------------------------------------
// Function Prototypes
// process (char *deckfname, char *msg, char * newmsg,  char mode); 


//-------------------------------------------------------
// Main
//-------------------------------------------------------
/**
* Main function that parses in commandline arguments and initializes the server
* @param argv[1] configurationFile
*/

    pthread_mutex_t mymutex = PTHREAD_MUTEX_INITIALIZER;
    int concurrency = 1;

int main(int argc, char *argv[])
{
/* TEMP FOR DEBUGGING
    addclient(20);
    printf("Top fd is %d\n", top->fd);
*/
/*
// These are in storage.c for M2, need to change for M4 when multi client
    int authen = 0; // single client server not authenticated
    int connected = 0; // single client server not connected
*/




    struct client *p, *nextp; // 2 pointers for the linked list
    struct client *p1, *p2; // for anything else to match up 
    pthread_t threads[20];
	int validThread[20] = {0};
    struct thread_data threaddata[20];
    int threadNum = 0;
    int threadErr = 0;
	int i = 0;

    int totalProcTime = 0;	// total Processing Time
    struct timeval start_time, end_time;	//Same as client.c implementation
    int tempT = 0;
    char timeOut[64];
    // Process command line arguments.
    // This program expects exactly one argument: the config file name.
    assert(argc > 0); // aborts program if this is false
    if (argc != 2)
    {
	printf("Usage %s <config_file>\n", argv[0]);
	exit(EXIT_FAILURE);
    }
    // Configuration file
    char *config_file = argv[1];
    // Read the config_file and store into the config_params struct
    struct config_params params; // structure to marshall and store configuration parameters into a structure
    struct check_duplmis check; // structure to check duplicates of configuration parameters
    initialCheck(&check);
    // @note Table is currently defined in server.c only
    // Initialize table as a sorted array
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////initializeSortedArrayTable();
    // @note CTRL+C automatically frees all memory according to T.A
    // Read the configuration file and check to make sure it meets the configuration file requirements

/*
Jerry's Task 1 Begin:
Modify read_config below that is in utils.h and defined in utils.c
such that it is able to accept the new table line structure

Look at Milestone 3 for examples
In short,
table format will be:
table nameOfTable col1Name:Type, col2Name:Type, ... , colXName:Type
// Note: X <= MAX_COLUMNS_PER_TABLE
// Note: Type can be a char[SIZE] which means when you create a name for this column,
// you need to create char name[SIZE] and make sure your input for later insertions does not exceed SIZE-1
Jerry's Task 1 END
*/
// THIS ENTIRE FUNC. HAS TO BE CHANGED TO CONFORM TO YACC & LEX
    int status = read_config(config_file, &params, &check); // returns 0 if successful
    if (status != 0)
    {
	printf("Error processing config file.\n");
	exit(EXIT_FAILURE);
    }
    // Sort the tables
    /////////////////////////////////////////////////////////////////////////sortTable(); // TODO: Change this to merge/quick sort instead of bubble sort
printf("\n\n\n\n\n\n\n\n\n\n"); 
printf("TABLE NAME:\n");
    printTableName();
    // Update concurrency
    concurrency = params.concurrency; 

	if(concurrency != 0 && concurrency != 1)		// ONLY IMPLEMENT THESE CONCURRENCIES
		exit(EXIT_FAILURE);
		
printf("concurrency: %d\n", concurrency); 


    // Performance Evaluation Variables
    struct timeval start_time1, end_time1;




	char deckfname[50] ="deck.txt"; 
//	char msg[50] = "DOABARRELROLL";  
//	char msg[50] = "LAKEHYLIA"; 
//	char msg[100] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";  
	char msg[100] = "ABCDEFG@HIJ,KLMN.OPQR STUV+WXYZabcdefghijklmnopqrstuvwxyz";  	 	 
	char newmsg[100] = "";
	char finalmsg[100] = "";  
	char mode = 'e';
printf("Message Encrypt:%s\n", msg); 
    gettimeofday(&start_time1, NULL);
 process (deckfname, msg, newmsg,  mode); 
//	strcpy(msg, "OXXIKQCPSZXWW"); 
printf("Message Decrypt:%s\n", newmsg); 
gettimeofday(&end_time1, NULL);		//End of time
tempT = ((end_time1.tv_sec - start_time1.tv_sec)*1000000L+end_time1.tv_usec) - start_time1.tv_usec;
printf("Total Encrypt time is %d\n", tempT);
	mode = 'd'; 
    gettimeofday(&start_time1, NULL);
 process (deckfname, newmsg, finalmsg,  mode); 
gettimeofday(&end_time1, NULL);		//End of time
tempT = ((end_time1.tv_sec - start_time1.tv_sec)*1000000L+end_time1.tv_usec) - start_time1.tv_usec;
printf(" Total Decrypt time is %d\n", tempT);
printf("FinalMessage:%s\n", finalmsg); 
printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); 
printf("\n\n\n\n\n\n\n\n\n\n                              SERVER\n\n\n\n\n\n"); 
// Calculate Performance time
 
    // Set up time for logging
    char fileName[MAX_STRTYPE_SIZE];
    time_t basetime;
    struct tm * conTime;
    basetime = time(NULL);
    conTime = localtime(&basetime);
    strftime(fileName, sizeof(fileName), "Server-%Y-%m-%d-%H-%M-%S.log",conTime);
    int logChoice = LOGGING;
    // Initialize fileServer based on LOGGING
    switch(logChoice)
    {
	case 0: // Disable Logging
	    fileServer = NULL;
	    break;
	case 1: // Log to stdout
	    fileServer = stdout;
	    break;
	case 2: // Log to File
	    // Append file for appending an update
	    fileServer = fopen (fileName,"a+");
	    if (fileServer == NULL)
	    {
		printf("Error opening file.\n");
		fclose(fileServer);
		exit(EXIT_FAILURE);
	    }
	    break;
    }
    // Server on localhost:1376
//    sprintf(message, "Server on %s:%d\n", params.server_host, params.server_port);
  //  logger (fileServer, message);
    // Create a socket for the server
    // listensock is the sockfd to be listening to
    int listensock = socket(PF_INET, SOCK_STREAM, 0);
    if (listensock < 0)
    {
	printf("Error creating socket.\n");
	Fclose(logChoice, 1);
	exit(EXIT_FAILURE);
    }
    // Allow listening port to be reused if defunctional
    int yes = 1;
    status = setsockopt(listensock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes);
    if (status != 0)
    {
	printf("Error configuring socket.\n");
	Fclose(logChoice, 1);
	exit(EXIT_FAILURE);
    }
    // Bind to the listening port.
    struct sockaddr_in listenaddr;
    memset(&listenaddr, 0, sizeof listenaddr);
    listenaddr.sin_family = AF_INET;
    listenaddr.sin_port = htons(params.server_port);
    inet_pton(AF_INET, params.server_host, &(listenaddr.sin_addr)); // bind to local IP address
    // Let server bind to this socket
    status = bind(listensock, (struct sockaddr*) &listenaddr, sizeof listenaddr);
    if (status != 0)
    {
	printf("Error binding socket.");
	Fclose(logChoice, 1);
	exit(EXIT_FAILURE);
    }
    // Start listening for connections.
    status = listen(listensock, MAX_LISTENQUEUELEN);
    if (status != 0)
    {
	printf("Error listening on socket.\n");
	Fclose(logChoice, 1);
	exit(EXIT_FAILURE);
    }
    // Listen loop.
    int wait_for_connections = 1;
	// Wait for a connection.
	struct sockaddr_in clientaddr;
	socklen_t clientaddrlen = sizeof clientaddr;
	int clientsock;
    // Infinite while loop for milestone 1


//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
	if(concurrency == 0)
	{
    while (wait_for_connections)
    {

	// Accept any calls from any client
	// @note: Will just poll till a client is accepted
	// @note: Right now only works with 1 clientsock
	 clientsock = accept(listensock, (struct sockaddr*)&clientaddr, &clientaddrlen);
	// Add this client to linked list of clients
	addclient(clientsock); 
	// Accepted client wil return it's file descriptor as clientsock
	if (clientsock < 0)
	{
	    printf("Error accepting a connection.");
	    Fclose(logChoice, 1);
	    exit(EXIT_FAILURE);
	}
//	sprintf(message, "Got a connection from %s:%d.\n", inet_ntoa(clientaddr.sin_addr), clientaddr.sin_port);
//	logger(fileServer, message);
	    // TODO: For Milestone 3, change code to work with > 1 client
	    // Get commands from client.
	    int wait_for_commands = 1;
	    do
	    {
	        // Read a line from the client.
	        char cmd[MAX_CMD_LEN];
	        // Read from clientsock's file descriptor to command
	        int status = recvline(clientsock, cmd, MAX_CMD_LEN);
		if (status != 0)
		{
		    // Here, either an error occurred or the client closed the connection.
		    // Stop waiting for commands
		    wait_for_commands = 0;
		}
		else
		{
		    // Handle the command from the client.
		    // @note: For milestone1, this just prints it back out to server
		    //********** TESTING ONLY
		    //**********/
		    int status = handle_command(clientsock, cmd, &params);
		    // If handled command fails, stop waiting for command
		    if (status != 0)
		    {
		   	printf("Error: Handle command return status of -1 to main() in server");
			wait_for_commands = 0; // stop waiting for commands
			exit(EXIT_FAILURE); // @note original code for M1 by prof. was to just EXIT_SUCCESS at bottom
		    }
		}
	    }
	    while (wait_for_commands); //do while client is still interacting
	    // Close the connection with the client.
	    //close(clientsock);
//	    sprintf(message, "Closed connection from %s:%d.\n", inet_ntoa(clientaddr.sin_addr), clientaddr.sin_port);
//	    logger(fileServer, message);
	} // end of while wait for connections   for M3 code  // Stop listening for connections.
	}	// If loop for if using threads
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
	else if (concurrency == 1)
	{
    while (wait_for_connections)
    {

	// Accept any calls from any client
	// @note: Will just poll till a client is accepted
	// @note: Right now only works with 1 clientsock
	 clientsock = accept(listensock, (struct sockaddr*)&clientaddr, &clientaddrlen);
	// Add this client to linked list of clients
	addclient(clientsock); 
	// Accepted client wil return it's file descriptor as clientsock
	if (clientsock < 0)
	{
	    printf("Error accepting a connection.");
	    Fclose(logChoice, 1);
	    exit(EXIT_FAILURE);
	}
//	sprintf(message, "Got a connection from %s:%d.\n", inet_ntoa(clientaddr.sin_addr), clientaddr.sin_port);
//	logger(fileServer, message);
	    //Thread create here
	    for(i=0;i<MAX_CONNECTIONS;i++)
	    {
		// Get total number of current threads 
		if(validThread[i]==0)
		{
		    threadNum=i;
		    validThread[i]=1;
		    break;
		}
	    	if(i==MAX_CONNECTIONS-1)	// If last iteration, and not found, too many clients exit failure;
	    	{
		    sprintf(message, "Too many clients: error\n");
		    logger(fileServer, message);
		    exit(EXIT_FAILURE);
		}
	    }
	    threaddata[threadNum].params = &params;
	    threaddata[threadNum].clientaddr = clientaddr;
	    threaddata[threadNum].clientsock = clientsock;
	    threaddata[threadNum].threadNum = &validThread[i];
	    threadErr = pthread_create(&threads[threadNum],NULL,serverThreads, (void*) &threaddata[threadNum]);
	    threadNum++;
	    if(threadErr)
	    {
		sprintf(message, "ThreadError is %d\n", threadErr);
		logger(fileServer, message);
		exit(EXIT_FAILURE);
	    }
	} // END OF WHILE WAIT FOR CONNECtions   for threads  // Stop listening for connections.
	}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
	else if (concurrency == 2)
	{
	    while (wait_for_connections)
	    {
		printf("IN SELECT\n"); 
		fd_set fdlist;
		// Need to know maxfd for select()
		int maxfd = listensock;
		FD_ZERO(&fdlist); // clears the set
		FD_SET(listensock, &fdlist); // adds listenfd (host) to fdlist
		// continue moving as long p is not NULL 
		// TODO: MAKE IT LESS THAN MAX CONNECTION CLIENTS 
		// TODO: TRANSACTION STUFF 
		// TODO: WRITE TO FD AS WELL NOT JUST READ 
printf("HAHA1\n"); 
		for (p = top; p; p = p->nextpos) // NULL at end of linked list
		{
printf("HAHA2\n"); 
		    FD_SET(p->fd, &fdlist); // include everything into the fdlist
		    if (p->fd > maxfd)
			maxfd = p->fd; // update maxfd
printf("HAHA 3 maxfd is %d\n", maxfd); 
		}
printf("HAHA20\n"); 
		//===================================================
		// Select()
		//===================================================
		// don't write or do any exception, and no timeval. NEED CHANGE THIS TO DO WRITE 
		if ((select(maxfd + 1, &fdlist, NULL, NULL, NULL)) < 0) // returns -1 on error, 0 if timeout, and file descriptor
		{	// (numfd, readfd, writefd, exceptfd, timeval)
printf("HAHA4\n"); 
		    printf(" ERROR IN SELECT!!\n"); 
		    exit(EXIT_FAILURE);	
		}
printf("HAHA30\n"); 
		// No error occured, process select options
		// If listenfd has read, it means there is a new connection
		if (FD_ISSET(listensock, &fdlist)) // connect if new client is connecting
		{
printf("HAHA5\n"); 
		// If the host socket is still in the fdlist
		    newconnection(listensock);// accept connection & update linked list
		    // All Clients
printf("HAHA70\n"); 
		}
printf("HAHA40\n"); 
		for (p = top; p; p = nextp)
		{
printf("HAHA6\n"); 
		    nextp = p->nextpos; // in case we remove this client because of error
printf("HAHA61\n"); 
		    if (FD_ISSET(p->fd, &fdlist)) // if this client has buffer's to read
		    {
printf("HAHA62\n"); 
			// Read a line from the client.
			char cmd[MAX_CMD_LEN];
			int currentSock = p->fd; 
printf("HAHA63\n"); 
			// Read from clientsock's file descriptor to command
			int status = recvline(currentSock, cmd, MAX_CMD_LEN);
printf("HAHA64\n"); 
			if (status != 0)
			{
printf("HAHA7\n"); 
			    // Here, either an error occurred or the client closed the connection.
			    // Stop waiting for commands
			    // wait_for_commands = 0;
	//		    printf(" TODO: REMOVE CLIENT!!!!\n" ); 
				struct client *removeClient = getclient(currentSock); 
				close(removeClient->fd); 
				removeclient(removeClient);
				
			}
			else
			{
printf("HAHA8\n"); 
			    // Handle the command from the client.
			    // @note: For milestone1, this just prints it back out to server
			    //********** TESTING ONLY
			    //**********/
			    int status = handle_command(currentSock, cmd, &params);
printf("HAHA83\n"); 
			    // If handled command fails, stop waiting for command
			    if (status != 0)
			    {
				printf("Error: Handle command return status of -1 to main() in server");
				//	wait_for_commands = 0; // stop waiting for commands
				exit(EXIT_FAILURE); // @note original code for M1 by prof. was to just EXIT_SUCCESS at bottom
			    }
			}
		    }

		} // end of For
	    } // end of while(1) 
	} // end of concurrency = 2
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
	else if (concurrency == 3)
	{
	    printf("IN FORK, not implemented\n"); 
	    close(listensock);
    	    return EXIT_SUCCESS;
	}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
	else
	{
	    // Should never be here since lex and yacc fails it first
	    printf("ERROR, concurrency is not 0-3\n"); 
	    close(listensock);
    	    return EXIT_SUCCESS;
	}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
    close(listensock);
    return EXIT_SUCCESS;
}

//-------------------------------------------------------
// Helper Functions
//-------------------------------------------------------
/**
* This function handles a command given by sending it as a string to a socket given.
* @param sock The socket descriptor to output commands to
* @param cmd A string to be sent to a socket descriptor
* @returns 0 if success, returns error code if failure
*/
int handle_command(int sock, char *cmd, struct config_params *params)
{
    char table[MAX_TABLE_LEN] = "a", key[MAX_KEY_LEN+1] = "a", username[MAX_USERNAME_LEN+1] = "a", password[MAX_ENC_PASSWORD_LEN+1] = "a";
    struct storage_record stTemp;
    char value[sizeof stTemp.value];
    char* args[5];
	char data[100];
    // Get arguments into args and numArgs holds number of arguments given whic is 3 or 4.
    // @note: Need to free all args once done
    int numArgs = getArguments(args, cmd);
    // Note: Must free all args later
    int i = 0;
//    sprintf(message, "Processing command '%s'\n", cmd);
    logger(fileServer, message);
    // @note: sock is the socket file descriptor (sockfd)
    // Send the command out to sockfd, which is the server
    // and end with a new line
// printf("arguments receive are:\n%s %s %s %s\n", args[0], args[1], args[2], args[3]);
    // Handle Commands
    //--------------------------------------------
    // GET table key (NULL)
    //--------------------------------------------

    if (strcmp(args[0], "GET") == 0)
    {
	if(findTableName(args[1]) == 0)
	{
            sprintf(message, "-2\n"); // 1=> success
            sendall(sock, message, strlen(message));
	    return 0; // server shouldnt crash if client fails
	}
	
	int tokenOut = 0;

	int getStat = getValue(args[1], args[2], data, &tokenOut); // get the value on args[3] 
	switch (getStat)
	{
	    case -1:
		sprintf(message, "-1\n");
		sendall(sock, message, strlen(message));
		return 0; // server shouldn't crash if client fails 
		break;
	    case 0:
		sprintf(message, "0\n");
		sendall(sock, message, strlen(message));
		return 0; // server shouldn't crash if client fails 
		break;
	    case 1:
		// Send success and actual message together
		sprintf(message, "1@%s@%d\n", data,tokenOut);
		sendall(sock, message, strlen(message));
		return 0;
		break;
	    default: // should never be here
		break;
	}
    }

    //--------------------------------------------
    // SET table key value
    //--------------------------------------------
    if (strcmp(args[0], "SET") == 0)
    {
	if(concurrency == 1)
		pthread_mutex_lock(&mymutex);		// Lock
	if(findTableName(args[1]) == 0)
	{
            sprintf(message, "-2\n"); // 1=> success
            sendall(sock, message, strlen(message));
	    if(concurrency == 1)
			pthread_mutex_unlock(&mymutex);		// If exitting unlock
	    return 0;	// Server should not crash regardless 
	}
	// If given NULL string as input, args 3 should be NULL
	if (strcmp(args[3], "NULL") == 0 || strcmp(args[3], "") == 0)
	{
	   args[3] = NULL;
	}
	int insertStat;
	int i = 0;
	char** colParam;
	char** colName;
	char** colType;
	int numCol;
	char* tempkey = "InitialKey";
	int token = 0;
	token = atoi(args[4]);
	getValue(args[1], tempkey, data, &i); // Get data in InitialKey which is entry containing config data

	numCol = getCol(data);	// Number of columns
	colParam = (char**) malloc(sizeof(char**) * numCol);	// Initialize an array to store each column name
	for(i = 0; i < numCol; i++)
	{
		colParam[i] = (char*) malloc(sizeof(char*) * (MAX_COLNAME_LEN+11));
	}
	colName = (char**) malloc(sizeof(char**) * numCol);	// Initialize an array to store each column name
	for(i = 0; i < numCol; i++)
	{
		colName[i] = (char*) malloc(sizeof(char*) * (MAX_COLNAME_LEN+1));
	}
	colType = (char**) malloc(sizeof(char**) * numCol);	// Initialize an array to store each column name
	for(i = 0; i < numCol; i++)
	{
		colType[i] = (char*) malloc(sizeof(char*) * 11);
	}
	getArgumentsJer(colParam, data);	// Get column data
	separateParam(colParam, colName, colType, numCol);	// Separate column data
	if(args[3]==NULL||args[3]=="NULL"){
		if(concurrency == 1 || concurrency == 2 )
			insertStat = insertValueTok(args[1], args[2], args[3],token);
		else
		{


			insertStat = insertValue(args[1], args[2], args[3], ENCRYPTION_ON);
	//		insertStat = insertValue(args[1], newmsg, args[3]);
		}
	}
	else if(validParam(colName, args[3], colType, numCol))	//	Is it valid parameters?
	{
		if(concurrency == 1 || concurrency == 2)
			insertStat = insertValueTok(args[1], args[2], args[3],token);
		else
			insertStat = insertValue(args[1], args[2], args[3], ENCRYPTION_ON);
		i = strlen(args[3]);
		if(i > 799)
			i = -3;		//	Value too long
	}
	else
		insertStat = -3;

//------------------------------------------
int printTable = findTableIndex(args[1]); 
printRecord(printTable); 
//------------------------------------------


	for(i = 0; i < numCol; i++)	//	Free colParam
	{
		free(colParam[i]);

	}
	free(colParam);
	colParam=NULL;
	for(i = 0; i < numCol; i++)	//	Free colName
	{
		free(colName[i]);
		colName[i]=NULL;
	}
	free(colName);
	colName=NULL;
	for(i = 0; i < numCol; i++)					//	Free colType
	{
		free(colType[i]);
		colType[i]=NULL;
	}
	free(colType);
	colType=NULL;
	switch (insertStat)
	{
	    case -5:
		sprintf(message, "-5\n");
		sendall(sock, message, strlen(message));
	   	if(concurrency == 1)
			pthread_mutex_unlock(&mymutex);		// If exitting unlock
		return 0; // Server shouldnt crash regardless		
	    case -3:
		sprintf(message, "-3\n");
		sendall(sock, message, strlen(message));
	   	if(concurrency == 1)
			pthread_mutex_unlock(&mymutex);		// If exitting unlock
		return 0; // Server shouldnt crash regardless
	    case -1:
		sprintf(message, "-1\n");
		sendall(sock, message, strlen(message));
	   	if(concurrency == 1)
		pthread_mutex_unlock(&mymutex);		// If exitting unlock
		return 0; // server shouldnt crash regardless
		break;
	    case 1:
		sprintf(message, "1\n");
		sendall(sock, message, strlen(message));
	   	if(concurrency == 1)
			pthread_mutex_unlock(&mymutex);		// If exitting unlock
		return 0;
		break;
	    case 2:
		sprintf(message, "2\n");
		sendall(sock, message, strlen(message));
	   	if(concurrency == 1)
			pthread_mutex_unlock(&mymutex);		// If exitting unlock
		return 0;
		break;
	    case 3:
		sprintf(message, "3\n");
		sendall(sock, message, strlen(message));
	   	if(concurrency == 1)
			pthread_mutex_unlock(&mymutex);		// If exitting unlock
		return 0;
		break;
	    default:
		break;
	}
    }
    //--------------------------------------------
    // AUTH username encryptpassword (NULL)
    //--------------------------------------------
    if (strcmp(args[0], "AUTH") == 0)
    {
	if(concurrency == 1)
	{
		pthread_mutex_lock(&mymutex);		// Lock
	}
	if (strcmp(args[1], params->username) == 0)
	{
	    if(strcmp(args[2], params->password) == 0)
	    {
		struct client* newclient;
//TODO: account for case where client not connected, make a check_connect() and check_auth() function
		newclient = getclient(sock);
		strcpy(newclient->username, params->username);
		strcpy(newclient->cryptpasswd, params->password);
		newclient->auth = true;
		sprintf(message, "1\n"); // 1=> success
		sendall(sock, message, strlen(message));
	   	if(concurrency == 1)
			pthread_mutex_unlock(&mymutex);		// If exitting unlock
		return 0;
	    }
	}
	sprintf(message, "0\n");
	sendall(sock, message, strlen(message));
	if(concurrency == 1)
	{
		pthread_mutex_unlock(&mymutex);		// If exitting unlock	
	}
	return 0;
    }
	//--------------------------------------------
    // QUERY table predicates max_keys
    //--------------------------------------------
    if (strcmp(args[0], "QUE") == 0)
    {
	
	if(findTableName(args[1]) == 0)
	{
            sprintf(message, "-2\n"); // 1=> success
            sendall(sock, message, strlen(message));
	    return 0; // server must not crash regardless
	}
	int i = 0;
	char** predName;
	char* oper;
	char** predValue;
	char** colParam;
	char** colName;
	char** colType;
	char** keyOut;
	int numCol;
	int max_keys;
	char* tempkey = "InitialKey";
	int noError=1;
	int numKeysOut=0;
	int totalRecords=0;

	
//--------------------------------------------------------------------
//This portion gets the config data from the data structure
	getValue(args[1], tempkey, data, &i); // Get data in InitialKey which is entry containing config data

	numCol = getCol(data);	// Number of columns
	
	colParam = (char**) malloc(sizeof(char**) * numCol);	// Initialize an array to store each column name
	for(i = 0; i < numCol; i++)
	{
		colParam[i] = (char*) malloc(sizeof(char*) * (MAX_COLNAME_LEN+11));
	}
	
	colName = (char**) malloc(sizeof(char**) * numCol);	// Initialize an array to store each column name
	for(i = 0; i < numCol; i++)
	{
		colName[i] = (char*) malloc(sizeof(char*) * (MAX_COLNAME_LEN+1));
	}
	
	colType = (char**) malloc(sizeof(char**) * numCol);	// Initialize an array to store each column name
	for(i = 0; i < numCol; i++)
	{
		colType[i] = (char*) malloc(sizeof(char*) * 11);
	}
	
	getArgumentsJer(colParam, data);	// Get column data
	
	separateParam(colParam, colName, colType, numCol);	// Separate column data
	
//-----------------------------------------------------------------------------
//This for initializing and parsing from predicate string
	
	predName = (char**) malloc(sizeof(char**) * MAX_COLUMNS_PER_TABLE);	// Initialize an array to store each column name
	for(i = 0; i < MAX_COLUMNS_PER_TABLE; i++)
	{
		predName[i] = (char*) malloc(sizeof(char*) * (MAX_COLNAME_LEN+1));
	}
	
	oper = (char*) malloc(sizeof(char*) * (MAX_COLUMNS_PER_TABLE+1));	// Initialize an array to store each column name
	
	predValue = (char**) malloc(sizeof(char**) * MAX_COLUMNS_PER_TABLE);	// Initialize an array to store each column name
	for(i = 0; i < 10; i++)
	{
		predValue[i] = (char*) malloc(sizeof(char*) * (MAX_VALUE_LEN+1));
	}

	max_keys = atoi(args[3]);
	keyOut = (char**) malloc(sizeof(char**) * max_keys);	// Initialize an array to store each column name
	for(i = 0; i < max_keys; i++)
	{
		keyOut[i] = (char*) malloc(sizeof(char*) * (MAX_COLNAME_LEN+1));
	}
	
	if(args[2]!="NULL")
	{
	noError = getPred(args[2], predName, oper, predValue);
	noError = checkPredDuplicate(predName, oper);
	
	if(noError==1)
	{
		noError = validPredName(colName, colType, numCol, predName, predValue, oper);
	}
	
	
	if(noError)
	{
		totalRecords = findKeys(predName, predValue, oper, args[1], keyOut, max_keys, numCol);
		numKeysOut = findKeysCopiedNum(predName, predValue, oper, args[1], keyOut, max_keys, numCol);
	}
	}
	else
	{
		noError = 1;
		totalRecords = getAllKeys(args[1], keyOut, max_keys);
		numKeysOut = getAllKeysCopiedNum(args[1], keyOut, max_keys);
	}
	
//-----------------------------------------------------------------------
//This section deals with freeing memory	
	for(i = 0; i < MAX_COLUMNS_PER_TABLE; i++)	//	Free predName
	{
		free(predName[i]);

	}
	free(predName);
	predName=NULL;
		
	free(oper);
	oper=NULL;
	
	for(i = 0; i < MAX_COLUMNS_PER_TABLE; i++)					//	Free predValue
	{
		free(predValue[i]);
		predValue[i]=NULL;
	}
	free(predValue);
	predValue=NULL;
	
	for(i = 0; i < numCol; i++)	//	Free colParam
	{
		free(colParam[i]);

	}
	free(colParam);
	colParam=NULL;			
		
	for(i = 0; i < numCol; i++)	//	Free colName
	{
		free(colName[i]);
		colName[i]=NULL;
	}
	free(colName);
	colName=NULL;
	
	for(i = 0; i < numCol; i++)					//	Free colType
	{
		free(colType[i]);
		colType[i]=NULL;
	}
	free(colType);
	colType=NULL;
//----------------------------------------------------------------------------
	
	switch (noError)
	{
	    case 0:
		sprintf(message, "-3\n");
		sendall(sock, message, strlen(message));
		return 0; // server must not exit due to error from client
		break;
	    case 1:
		// Send success and actual message together
		sprintf(message, "%d", totalRecords);
		for(i = 0; i < numKeysOut; i++)
		{
			strcat(message, "@");
			strcat(message, keyOut[i]);
		}
		strcat(message, "\n");
		
		for(i = 0; i < max_keys; i++)	//	Free colName
		{
			free(keyOut[i]);
		}
		free(keyOut);
		sendall(sock, message, strlen(message));
		return 0;
		break;
	    default: // should never be here
		break;
	}
    }

    // @note free(args[i]) here will crash, not sure if need to free
    return 0;
}



void serverThreads(void* thread_data){
	struct thread_data* mydata;
	mydata = (struct thread_data*) thread_data;
	struct config_params* params;
	params = mydata->params;
	int clientsock = mydata->clientsock;
	struct sockaddr_in clientaddr = mydata->clientaddr;
	int* threadNum = mydata->threadNum;
	int wait_for_commands = 1;
	int status = 0;
	srand(time(NULL));	// Seed for counter intializations
	do
	{
	    // Read a line from the client.
	    char cmd[MAX_CMD_LEN];
	    // Read from clientsock's file descriptor to command
	    int status = recvline(clientsock, cmd, MAX_CMD_LEN);
	    if (status != 0)
	    {
		// Here, either an error occurred or the client closed the connection.
		// Stop waiting for commands
		wait_for_commands = 0;
	    }
	    else
	    {
		// Handle the command from the client.
		// @note: For milestone1, this just prints it back out to server
		//********** TESTING ONLY
		//**********/

		int status = handle_command(clientsock, cmd, params);
		// If handled command fails, stop waiting for command
		if (status != 0)
		{
		    printf("Error: Handle command return status of -1 to main() in server");
		    wait_for_commands = 0; // stop waiting for commands
		    exit(EXIT_FAILURE); // @note original code for M1 by prof. was to just EXIT_SUCCESS at bottom
		}
            }
         }
	while (wait_for_commands); //do while client is still interacting
        // Close the connection with the client.
//	close(clientsock);
	sprintf(message, "Closed connection from %s:%d.\n", inet_ntoa(clientaddr.sin_addr), clientaddr.sin_port);
	logger(fileServer, message);
	(*threadNum)--;
	pthread_exit(NULL);
    }





// This function reads and process one client p to see what commands or messages it has to say
static void read_process(struct client *p1)
{

}


// This function reads a line from a client's buffer
// and returns a char pointer to the first character of the line
// or NULL if the line is not fully filled
char *myreadline(struct client *p)
{

}



// This function returns the first pointer to the next occurence of /r/n, \r, or \n
// and NULL if it is not found within the limit
char* extractline(char *s, int limit)
{
}


